#include "StdAfx.h"
#include "policyFileParser.h"
#include <sstream>
#include <iostream>
#include <fstream>
#include <windows.h>

//////////////////////////////////////////////////////////////////////////
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	13/05/2013
//Uses uses policy files to determine what action to take
//Needs to keep a log of each action and observation that there has
//occurred, this is stored is a map where the key is the node name and
//the value is the outcome, this is used to build the search strings
//to search for in the policy file
//
//Update 1
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	14/05/2014
//New constructor that loads policy files into memory upon launch and uses
//files in memory to search policies using a map of filename to list of
//strings
//
//Update 2
//Author	:	Ross Conroy ross.conroy@tees.ac.uk
//Date		:	14/05/2014
//Pre-loading pre-loads the policy into memory stored as a hash_map 
//by splitting the policy strings into two parts, the key is the 
//combination part of the string and the value is the action part of
//the string.
//e.g. map<string, string>
//////////////////////////////////////////////////////////////////////////

PolicyFileParser::PolicyFileParser()
{
}

PolicyFileParser::PolicyFileParser(string actionPrefix, string observationPrefix, string policyFiles, string fileExtension, bool preloadFiles)
{
	_observationPrefix = observationPrefix;
	_actionPrefix = actionPrefix;
	_policyFilesDir = policyFiles;
	_fileExtension = fileExtension;

	if(preloadFiles)
	{
		cout << "Pre-Loading Files" << endl;
		PreloadedFiles();
		cout << "Files Loaded" << endl;
	}
}

//Pre-loads policy files
void PolicyFileParser::PreloadedFiles()
{
	int numberOfFiles = GetNumberOfFilesInFolder(_policyFilesDir, _fileExtension);

	for(int i = 0; i < numberOfFiles; i++)
	{
		string file = GetActionNodeFileNameFromTimeStep(i);
		list<string> lines = ParseTextFile(file);
		list<string> parents = GetParentNamesFromFile(lines); //Gets parents and preloads data
		list<string> policyLines = GetPolicyList(lines);

		hash_map<string, list<string>> policy;

		int numParents = parents.size();
		_parents[file] = parents;

		for(list<string>::iterator it = policyLines.begin(); it != policyLines.end(); it++)
		{
			string line = *it;

			stringstream key;

			int start = 0;
			int end;
			string delim = ",";

			stringstream ss;
			ss << line << delim;
			line = ss.str();

			list<string> actions;

			int i = 0;
			while((end = line.find(delim, start)) != string::npos )
			{ 				
				string tmp = line.substr(start, end-start);
				start = end + delim.length();
				if(i < numParents)
				{
					key << tmp << delim;
				}
				else
				{
					actions.push_back(tmp);
				}
				i++;
			}

			policy[key.str()] = actions;
		}

		_policies[file] = policy;
	}
}

//Converts the text file into a list of strings for future parsing
//Update, check if file has already been loaded into memory
list<string> PolicyFileParser::ParseTextFile(string inputFile)
{
	list<string> lines;

	//Checks if file has already been loaded into memory
	//if(_preloadedFiles.count(inputFile) > 0)
	//{
	//	lines = _preloadedFiles[inputFile];
	//}
	//else
	//{
	ifstream readFile;
	readFile.open(inputFile.c_str());
	if(readFile.is_open())
	{
		while(!readFile.eof())
		{
			string line;
			getline(readFile, line);
			lines.push_back(line);
		}

		readFile.close();
	}

	//_preloadedFiles[inputFile] = lines;
	//}	

	return lines;
}

//Used the time step and global action name to determine the name of
//the current action node for time step
string PolicyFileParser::GetActionNodeFileNameFromTimeStep(int timeStep)
{
	stringstream ss;
	ss << _policyFilesDir << _actionPrefix << timeStep << _fileExtension;
	return ss.str();
}

//Finds the parent names near the beginning of the file and returns a list
//of the names of the parents in the order they appear in the file
list<string> PolicyFileParser::GetParentNamesFromFile(list<string> inputStrings)
{
	list<string> parents;

	bool readLine = false;
	bool breakfor = false;

	for(list<string>::iterator it = inputStrings.begin(); it != inputStrings.end(); it++)
	{
		string line = *it;

		//reached end of parents
		if(line == "POLICY")
		{
			readLine = false;
			breakfor = true;
		}

		if(breakfor)
		{
			break;
		}

		//If safe to read a line extract the parent name from it
		if(readLine)
		{
			string parentLine = line;
			int pos = parentLine.find_first_of(",");
			parentLine = parentLine.substr(0, pos);
			parents.push_back(parentLine);
		}

		//Hit start of parents
		if(line == "PARENTS")
		{
			readLine = true;
		}
	}

	return parents;
}

//Takes a list of parent nodes in order and uses the global map
//of past actions to build the search string.
string PolicyFileParser::GetCSVSearchString(list<string> parents)
{
	stringstream ss;

	for(list<string>::iterator it = parents.begin(); it != parents.end(); it++)
	{
		string key = *it;
		string value = _previousActObs[key];

		//cout << "Key - " << key << " Value - " << value << endl;

		ss << value << ",";
	}

	return ss.str();
}

//Searches the file and returns the line that contains the search string
string PolicyFileParser::GetFullStringFromSearchString(string searchString, list<string> lines)
{
	string line = "ERROR";

	for(list<string>::iterator it = lines.begin(); it != lines.end(); it++)
	{
		string l = *it;

		//Check if contains string
		if(l.find(searchString) != string::npos)
		{
			return l;
		}
	}

	return line;
}

//gets a list of actions from the policy, uses the number of parents to determine
//how many items are before the actions
list<string> PolicyFileParser::GetActionsFromLine(int numParents, string line)
{
	list<string> items;
	list<string> actions;

	stringstream ss;

	int start = 0;
	int end;
	string delim = ",";

	ss << line << delim;
	line = ss.str();

	int i = 0;
	while((end = line.find(delim, start)) != string::npos )
	{ 
		string action = line.substr(start, end-start);
		items.push_back(action); 
		start = end + delim.length();
		if(i >= numParents)
		{
			actions.push_back(action);
		}
		i++;
	}

	return actions;
}

//Uses the list of actions and chooses one at random;
string PolicyFileParser::GetActionFromList(list<string> actions)
{
	string action = "ERROR";
	int numActions = actions.size();

	if(numActions = 1)
	{
		action = *actions.begin();
	}
	else
	{

	}

	return action;
}

//Gets the list of strings that only relates to the policy
list<string> PolicyFileParser::GetPolicyList(list<string> inputStrings)
{
	list<string> policy;	

	bool readLine = false;

	for(list<string>::iterator it = inputStrings.begin(); it != inputStrings.end(); it++)
	{
		string line = *it;

		if(readLine)
		{			
			policy.push_back(line);
		}
		else
		{
			if(line == "POLICY")
			{
				readLine = true;
			}
		}
	}

	return policy;
}

//Uses the policy files to determine what action to take,
//Also stores the action and observation after determining the action to take
//Steps
//1.Determine appropriate text policy file to read
//2.Get parents, and order from text file
//3.Use Parents list names to get values from map
//4.Combine values from map into CSV representing combination of actions/observations
//5.Search policy for generated CSV and get the actions (Actions will be after CSV and CSV of actions, normally just single action)
//6.Set Action to result from policy file
//7.Store observation and policy for future use
//8.Return action
string PolicyFileParser::DecisionFromObservation(int timeStep, string observation)
{
	//Default Error, if successful then ERROR will be replaced

	if(timeStep == 0)
	{
		_previousActObs.clear();
	}

	string action = "ERROR"; 
	StoreObservation(timeStep, observation);	

	string actionFileName = GetActionNodeFileNameFromTimeStep(timeStep);

	if(_policies.count(actionFileName) > 0)
	{
		//hash_map<string, list<string>> policy = _policies[actionFileName];
		string searchStr = GetCSVSearchString(_parents[actionFileName]);
		//list<string>actions = policy[searchStr];
		list<string>actions = _policies[actionFileName][searchStr];
		action = GetActionFromList(actions);
	}
	else
	{
		string actionFileName = GetActionNodeFileNameFromTimeStep(timeStep);
		list<string> lines = ParseTextFile(actionFileName);
		list<string> parents = GetParentNamesFromFile(lines);
		string searchStr = GetCSVSearchString(parents);

		list<string> policy = GetPolicyList(lines);
		string line = GetFullStringFromSearchString(searchStr, policy);	

		if(line != "ERROR")
		{
			list<string> actions = GetActionsFromLine(parents.size(), line);
			action = GetActionFromList(actions);		
		}
	}

	StoreAction(timeStep, action);

	cout << "Time Step - " <<timeStep <<  "  Observation - " << observation << "  Action - " << action << endl;

	return action;
}

//Stores the action outcome for a time-step into the global map
void PolicyFileParser::StoreAction(int timeStep, string action)
{
	stringstream ss;
	ss << _actionPrefix;
	ss << timeStep;
	string actionKey = ss.str();
	_previousActObs[actionKey] = action;
	ss.clear();
}

//Stores the observation for a time-step into the global map
void PolicyFileParser::StoreObservation(int timeStep, string observation)
{
	stringstream ss;
	ss << _observationPrefix;
	ss << timeStep;
	string obsKey = ss.str();
	_previousActObs[obsKey] = observation;
	ss.clear();
}

//Determines how many policy files are available
int PolicyFileParser::GetNumberOfFilesInFolder(string folderDir, string fileExtension)
{
	int i = 0;
	ifstream readFile;
	bool end = false;	

	while(!end)
	{
		stringstream ss;
		ss << folderDir << _actionPrefix << i << fileExtension;
		string file = ss.str();

		readFile.open(file.c_str());
		if(readFile.is_open())
		{
			i++;
		}
		else
		{
			end = true;
		}

		readFile.close();
		ss.clear();
	}

	return i;

}